Skip to content

modules: re-point twap-monitor onto pool submit via the cow adapter - #469

Merged
mfw78 merged 2 commits into
dev/m1from
feat/m4-twap-on-pool
Jul 23, 2026
Merged

modules: re-point twap-monitor onto pool submit via the cow adapter#469
mfw78 merged 2 commits into
dev/m1from
feat/m4-twap-on-pool

Conversation

@mfw78

@mfw78 mfw78 commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

What

Re-points twap-monitor from the legacy shepherd:cow/CowApiHost world onto pool submit and status through the cow venue adapter: adds videre_sdk::keeper wiring (typed CowClient, body version 1), drops the direct cow-api import and the shepherd-sdk cow client dependency, and subscribes to intent-status for submitted-order polling. Per-chain adapter manifests (sepolia, load-mock) are added and every twap-wired engine config is pointed at the manifest matching the chain it indexes, since the adapter fixes its orderbook from the manifest chain at init.

Why

Wave I of #138: twap's chassis port (#147) landed on the legacy CowApiHost seam; this is the second half, re-pointing it onto the generic pool seam per M4.

Testing

Dispatch tests assert behaviour identity against the legacy bridge at the VenueTransport seam (journal, gate, retry effects, throttle-hint backoff, appData digest). Bundle boot proof moves to the videre-host platform suite (twap against the installed cow adapter); the cow-api boot-order invariant re-pins on ethflow-watcher.

AI Assistance

Implemented with Claude Code assistance; human-reviewed.

Closes #327

@lgahdl lgahdl left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid module migration — verified all seven touched engine configs correctly pair the Sepolia cow-venue manifest with twap-monitor (whose chain is pinned in its own module.toml, not derived from the engine's [chains.*] block, so no mismatch there), the dispatch tests genuinely substantiate the claimed behavior-identity coverage (journal short-circuit, gates, retry classification, throttle-hint backoff, appData digest — all checked against real assertions, not just renamed), the new intent-status subscription is purely additive with no duplicate polling, and every cow-api/CowApiHost reference is either removed or correctly scoped to ethflow-watcher (intentionally still on the legacy path). Two things worth a look:

Comment thread crates/videre-host/tests/platform.rs Outdated
let config = EngineConfig {
adapters: vec![AdapterEntry {
path: adapter_wasm,
manifest: Some(workspace_path("crates/cow-venue/module.toml")),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new e2e test — the one meant to prove the keeper+adapter pairing end-to-end — wires twap-monitor (Sepolia-pinned, chain_id = 11155111) against module.toml, the mainnet-default manifest, not module.sepolia.toml. It passes today only because the dispatched block hits the chainless-poll fault path with no watches indexed, so no submission ever happens. If a future change causes this exact test wiring to attempt a real submit, it would silently target the wrong chain's orderbook and this test wouldn't catch it — the manifest-matching invariant this whole PR is built around is untested here. Worth pointing this test at module.sepolia.toml.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, and it was worse than described. Fixed in this PR: the e2e test now installs crates/cow-venue/module.sepolia.toml, with a comment saying why the pair has to name the same chain.

Worse how: the test was not merely passing for the wrong reason, it was not running at all. module_wasm_or_skip("cow-venue") was returning None, so it early-returned and reported ok. The cow adapter wasm is built by a second, separate cargo build ... -p cow-venue --features cow-venue/adapter invocation that #467 added to CI, and my local gate battery was still building only the 17 module wasms. So locally the test had been a no-op since #467. CI does build it, so CI was covering it, but I have corrected my battery to build all 18 and to assert the count.

With the adapter wasm actually present the test runs in 2.0s rather than 0.007s, and passes against the Sepolia manifest.

Note it still does not assert a submit, so the manifest-matching invariant is exercised only to the extent that boot and dispatch succeed with the correct pair. Making it prove a real submission needs a mocked orderbook, which is a larger change than this car should carry.

@lgahdl lgahdl left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more thing worth flagging, on the architecture side rather than this diff's own code: CowVenue::ID is a fixed VenueId::from_static("cow") (pre-existing, unchanged here), and the adapter fixes its orderbook chain at init with no chain parameter on any VenueAdapter method. Combined, that means two adapter instances installed simultaneously (e.g. mainnet + Sepolia in one engine process) would register under the same venue id with nothing to disambiguate them at the pool router. Today's per-chain-manifest approach works because each deployment only ever installs one chain's adapter, but that's a real single-process-multi-chain limitation this PR's manifests are compensating for, not just a configuration convenience — and it isn't documented anywhere as a known constraint (the manifest/Dockerfile comments read as a factual description, not a flagged limitation). Worth a one-line ADR/doc note naming single-process-multi-chain as an explicit non-goal for M4, or a chain-qualified venue-id scheme now while the surface is still small, before a second chain's adapter needs to coexist with the first in one process.

The module flips from the shepherd:cow world onto #[videre_sdk::keeper]:
the manifest declares the client capability and body version 1, the
keeper run submits through the typed CowClient over the module's own
videre:venue/client import, and the direct cow-api import and legacy
cow client bridge drop out. Status transitions the registry polls back
arrive on a cow intent-status subscription.

Behaviour identity is proven at the VenueTransport seam: the dispatch
tests script submit outcomes against the mock host and assert the same
journal, gate, and retry effects as the legacy bridge, including the
throttle hint surviving as an epoch backoff and the appData digest
riding the body verbatim. The bundle boot proof moves to the videre
platform suite (twap against the installed cow adapter); the cow-api
boot-order invariant re-pins on ethflow-watcher. Engine configs that
boot twap install the bundled adapter.
The adapter fixes its orderbook at init from its manifest chain, so a
Sepolia run wired to the mainnet manifest submits to the wrong
orderbook. Add per-chain manifest variants (sepolia, load-mock) and
point every twap-wired engine config at the one matching the chain it
indexes.
@mfw78
mfw78 force-pushed the feat/m4-twap-on-pool branch from 3b82047 to f2057f9 Compare July 23, 2026 07:08
@mfw78

mfw78 commented Jul 23, 2026

Copy link
Copy Markdown
Contributor Author

Agreed, and confirmed the constraint survives to the end-of-train tip: CowVenue::ID is still VenueId::from_static("cow") (cow-venue/src/client.rs:26) with no chain component, and the adapter still fixes its chain at init.

I took your first option and folded it into this PR, since the limitation is exactly what this car's manifests are compensating for. docs/deployment/multi-chain.md now names it. That doc was the right place because its intro actively implied the opposite: it opened by saying a single nexum process can serve modules watching several chains at once, with no distinction drawn between keeper modules, which really do subscribe per chain, and venue adapters, which do not. The new text draws that line, explains that two cow adapters would register under the same id with nothing at the pool router to tell them apart, records single-process multi-chain submission as an explicit M4 non-goal, and gives the workaround of one engine process per submitting chain paired with the matching manifest.

Your second option, chain-qualifying the venue id, is filed as nullislabs/videre-nexum-module#12 rather than folded. The surface is wider than the id itself: VenueId and the pool router in videre-host, Venue::ID and the typed client in videre-sdk, cow-venue's marker and its three manifests, the venue-agnostic gate charter, and every module manifest [venue] stanza. It is a breaking change to the routing key with M4 cars still in flight. I did flag on the issue that if it is wanted at all it should land before M5, since after the carve it becomes a coordinated change across two repos.

@mfw78
mfw78 merged commit b399759 into dev/m1 Jul 23, 2026
7 checks passed
@mfw78
mfw78 deleted the feat/m4-twap-on-pool branch July 23, 2026 07:54
mfw78 added a commit that referenced this pull request Jul 27, 2026
…469)

* twap: re-point the monitor onto pool submit through the cow adapter

The module flips from the shepherd:cow world onto #[videre_sdk::keeper]:
the manifest declares the client capability and body version 1, the
keeper run submits through the typed CowClient over the module's own
videre:venue/client import, and the direct cow-api import and legacy
cow client bridge drop out. Status transitions the registry polls back
arrive on a cow intent-status subscription.

Behaviour identity is proven at the VenueTransport seam: the dispatch
tests script submit outcomes against the mock host and assert the same
journal, gate, and retry effects as the legacy bridge, including the
throttle hint surviving as an epoch backoff and the appData digest
riding the body verbatim. The bundle boot proof moves to the videre
platform suite (twap against the installed cow adapter); the cow-api
boot-order invariant re-pins on ethflow-watcher. Engine configs that
boot twap install the bundled adapter.

* cow: match the adapter manifest chain to each engine config's run

The adapter fixes its orderbook at init from its manifest chain, so a
Sepolia run wired to the mainnet manifest submits to the wrong
orderbook. Add per-chain manifest variants (sepolia, load-mock) and
point every twap-wired engine config at the one matching the chain it
indexes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants